package util;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.*;

import javax.swing.DefaultListModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;

import Server.TCPServer;

import com.sun.corba.se.spi.orbutil.fsm.Input;

import mill.*;
import mill.components.MillComponent;
import mill.measurePoints.MeasurePoint;


public class Model implements Serializable{

	private List<MillPark> m_parks;
	private MillFactory m_millFac;
	private MillPark m_selectedPark;
	private final String FILENAME = "millParks.startOnLoad";
	private Logger m_logger = Logger.Instance;

	public Model() {
		loadMillParks();		
		
		m_millFac = new MillFactory();
		m_logger.set_parks(m_parks);
		m_logger.start();
		
		TCPServer tcpsrv = new TCPServer(8000,this, m_logger);
		tcpsrv.start();
		
	}

	public void serializeMillParks()
	{
		try {
			// Create file output stream.			
			FileOutputStream fstream = new FileOutputStream(FILENAME);

			try {
				// Create object output stream.
				ObjectOutputStream ostream = new ObjectOutputStream(fstream);

				try {
					// Write object.
					ostream.writeObject(m_parks);
					ostream.flush();
	
				} finally {
					// Close object stream.
					ostream.close();
				}
			} finally {
				// Close file stream.
				fstream.close();
			}
		} catch(Exception ex) {
			System.out.println(ex);
		}
	}
	
	public void loadMillParks()
	{
		try{
			InputStream is = new FileInputStream(FILENAME);
			InputStream buffer = new BufferedInputStream(is);
			ObjectInput input = new ObjectInputStream(buffer);

			try{
				m_parks = (List<MillPark>)input.readObject();	
				input.close();
				
				for(MillPark mp : m_parks)
				{
					Simulator windSim = new Simulator(mp);
					windSim.start();
				}
				
			}
			catch(Exception e)
			{
				m_parks = new ArrayList<MillPark>();
			}
		}
		catch(Exception e)
		{
			m_parks = new ArrayList<MillPark>();
		}
		
	}
	
	public void changeSelected(MillPark park){
		m_selectedPark = park;
	}
	
	public void CreateMillPark(int serialNumber, String location){

		MillPark park = new MillPark(serialNumber, location);
		Simulator windSim = new Simulator(park);
		windSim.start();

		m_parks.add(park);
		serializeMillParks();
	}

	public boolean removeMill(IMill mill)
	{
		boolean succes = false;
		succes = m_selectedPark.removeMill(mill);
		serializeMillParks();
		return succes;
	}

	public void addMill(int id, MillType type)
	{
		m_selectedPark.addMill(m_millFac.createMill(id, type));
		serializeMillParks();
	}

	public boolean removeMillComponent(IMill mill, MillComponent mc)
	{
		boolean succes = false;
		succes = ((IMill)(m_selectedPark.getMills().get(m_selectedPark.getMills().indexOf(mill)))).getComponents().remove(mc);
		serializeMillParks();
		return succes;
	}

	public boolean removeMeasurePoint(IMill mill, MeasurePoint mp)
	{
		boolean succes = false;
		succes = ((IMill)(m_selectedPark.getMills().get(m_selectedPark.getMills().indexOf(mill)))).getMeasurePoints().remove(mp);
		serializeMillParks();
		return succes;
	}

	public boolean removeMillPark()
	{
		boolean succes = false;	
		succes = m_parks.remove(m_selectedPark);	
		serializeMillParks();
		return succes;
	}

	public List<MillPark> get_MillParks()
	{
		return m_parks;
	}

	public int findMillParkIndex(MillPark park)
	{
		return m_parks.indexOf(park);
	}

	public DefaultTableModel get_TableModel(int index)
	{
		DefaultTableModel m_mainTable = new DefaultTableModel();

		m_mainTable.addColumn("Mill id");
		m_mainTable.addColumn("Output");
		m_mainTable.addColumn("Type");
		m_mainTable.addColumn("Height");
		m_mainTable.addColumn("Wingspan");
		m_mainTable.addColumn("Status");

		Object[] data = new Object[6];

		for(IMill mill : m_parks.get(index).getMills())
		{
			data[0] = "" + mill.getSerialNumber();
			data[1] = "" + mill.getPowerOutput() + " W";
			data[2] = "" + mill.getType().toString();
			data[3] = "" + mill.getHeight() + "m";
			data[4] = "" + mill.getWingSpan() + "m";
			data[5] = "" + mill.getStatus().toString();

			m_mainTable.addRow(data);
		}

		return m_mainTable;
	}

	public DefaultTableModel get_TableModelForLog(String filename)
	{
		List<MillLog> millLog;
		DefaultTableModel m_mainTable = new DefaultTableModel();
		Object[] data = new Object[6];

		m_mainTable.addColumn("Mill id");
		m_mainTable.addColumn("airTemperature");
		m_mainTable.addColumn("windDirection");
		m_mainTable.addColumn("windSpeed");
		m_mainTable.addColumn("powerOutput");
		m_mainTable.addColumn("timeStamp");

		try{
			InputStream is = new FileInputStream("Log/"+filename);
			InputStream buffer = new BufferedInputStream(is);
			ObjectInput input = new ObjectInputStream(buffer);

			try{
				millLog = (List<MillLog>)input.readObject();	
				input.close();
			}
			catch(Exception e)
			{
				millLog = new ArrayList<MillLog>();
			}
		}
		catch(Exception e)
		{
			millLog = new ArrayList<MillLog>();
		}

		DecimalFormat twoPlaces = new DecimalFormat("0.00");

		for(MillLog log : millLog)
		{
			data[0] = "" + log.getSerialNumber();
			data[1] = "" + twoPlaces.format(log.getAirTemperature());
			data[2] = "" + log.getWindDirection();
			data[3] = "" + log.getWindSpeed();
			data[4] = "" + log.getPowerOutput();
			data[5] = "" + log.getTimeStamp();

			m_mainTable.addRow(data);
		}

		return m_mainTable;
	}

	public DefaultListModel get_ListOfLogModel()
	{
		DefaultListModel listModel = new DefaultListModel();

		ResultSet rs;

		try {
			rs = DatabaseManager.Instance.selectAll();
			while(rs.next())
			{
				LogConfiguration log = new LogConfiguration();
				log.set_id(rs.getInt("id"));
				log.set_created(rs.getTimestamp("created"));
				log.set_numberOfEntries(rs.getInt("numberOfInputs"));
				log.set_timeBetweenEntries(rs.getInt("timeBetweenInputs"));
				log.set_filename(rs.getString("filename"));

				listModel.addElement(log);

			}
		} catch (SQLException e) {

		}

		return listModel;
	}

	public DefaultMutableTreeNode get_TreeModel()
	{
		DefaultMutableTreeNode top = new DefaultMutableTreeNode("Mill Parks");

		createTreeNodes(top);

		return top;
	}

	private void createTreeNodes(DefaultMutableTreeNode top)
	{
		DefaultMutableTreeNode parks = null;
		DefaultMutableTreeNode mills = null;
		DefaultMutableTreeNode components = null; 
		DefaultMutableTreeNode measurePoints = null;  
		DefaultMutableTreeNode componentsContainer = null;
		DefaultMutableTreeNode mpointsContainer = null;

		for(MillPark millPark : m_parks)
		{
			parks = new DefaultMutableTreeNode(millPark);
			top.add(parks);

			for(IMill mill : millPark.getMills())
			{
				mills = new DefaultMutableTreeNode(mill);
				parks.add(mills);

				components = new DefaultMutableTreeNode("Components");

				for(MillComponent mc : mill.getComponents())
				{
					componentsContainer = new DefaultMutableTreeNode(mc);
					components.add(componentsContainer);
				}
				componentsContainer = new DefaultMutableTreeNode("New Component");
				components.add(componentsContainer);
				mills.add(components);

				
				measurePoints = new DefaultMutableTreeNode("Measure Points");
				for(MeasurePoint mp : mill.getMeasurePoints())
				{
					mpointsContainer = new DefaultMutableTreeNode(mp);
					measurePoints.add(mpointsContainer);
				}
				mpointsContainer = new DefaultMutableTreeNode("New Measure Point");
				measurePoints.add(mpointsContainer);
				mills.add(measurePoints);
			}

			mills = new DefaultMutableTreeNode("New Windmill");
			parks.add(mills);


		}
		parks = new DefaultMutableTreeNode("New Mill Park");
		top.add(parks);

	}
	
}
